﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Windows.Data;
using System.Windows.Input;
using AnthStat.Domain;
using AnthStat.WPF.Applications.Collections;

namespace AnthStat.WPF.Applications
{
    [DebuggerDisplay("Name = {FullName} Sex = {Sex}")]
    public sealed class PersonViewModel : ViewModelBase
    {
        #region Members
        private readonly Person _person;
        private readonly MeasurementCollection _measurementCollection;
        private bool _isShowingRemoveMeasurementPrompt = false;
        private readonly object _measurementsLock = new object();
        #endregion Members

        #region Constructors
        public PersonViewModel(Person person)
        {
            // pre
            Contract.Requires(person != null);

            // post
            Contract.Ensures(_measurementCollection != null);
            Contract.Ensures(_person != null);

            _person = person;
            _measurementCollection = new MeasurementCollection();

            System.Windows.Data.BindingOperations.EnableCollectionSynchronization(_measurementCollection, _measurementsLock);

            foreach (var mVm in _person.Measurements.Select(m => new MeasurementViewModel(m)))
            {
                _measurementCollection.Add(mVm);
            }

            RaisePropertyChanged("Sex");
            Sex = "M";
        }
        #endregion // Constructors

        #region Properties

        public bool HasMeasurementErrors
        {
            get
            {
                foreach (MeasurementViewModel mVm in _measurementCollection)
                {
                    if (mVm.HasErrors)
                    {
                        return true;
                    }
                }
                return false;
            }
        }

        public override bool HasErrorsOnSelfOrOwnedObjects
        {
            get
            {
                if (HasErrors) return true;
                return HasMeasurementErrors;
            }
        }

        public bool IsShowingRemoveMeasurementPrompt
        {
            get { return _isShowingRemoveMeasurementPrompt; }
            set
            {
                if (_isShowingRemoveMeasurementPrompt != value)
                {
                    _isShowingRemoveMeasurementPrompt = value;
                    RaisePropertyChanged("IsShowingRemoveMeasurementPrompt");
                }
            }
        }

        public ICollectionView MeasurementsView { get; set; }

        public MeasurementViewModel LastMeasurement
        {
            get
            {
                return _measurementCollection.LastOrDefault();
            }
        }

        public bool IsMeasureable
        {
            get
            {
                return _person.IsMeasureable;
            }
        }

        public string UserCreated
        {
            get
            {
                return _person.UserCreated;
            }
        }

        public string UserUpdated
        {
            get
            {
                return _person.UserUpdated;
            }
        }

        public string Team
        {
            get
            {
                return _person.Team;
            }
            set
            {
                if (!_person.Team.Equals(value))
                {
                    _person.Team = value;
                    RaisePropertyChanged("Team");
                }
            }
        }

        public string Household
        {
            get
            {
                return _person.Household;
            }
            set
            {
                if (!_person.Household.Equals(value))
                {
                    _person.Household = value;
                    RaisePropertyChanged("Household");
                }
            }
        }

        public int? Cluster
        {
            get
            {
                if (_person.Cluster == Int32.MinValue) return null;
                else return _person.Cluster;
            }
            set
            {
                if (!_person.Cluster.Equals(value))
                {
                    if (!value.HasValue)
                    {
                        _person.Cluster = Int32.MinValue;
                    }
                    else
                    {
                        _person.Cluster = value.Value;
                    }
                    RaisePropertyChanged("Cluster");
                }
            }
        }

        public string FirstName
        {
            get
            {
                return _person.FirstName;
            }
            set
            {
                if (!_person.FirstName.Equals(value))
                {
                    _person.FirstName = value;
                    RaisePropertyChanged("FirstName");
                    RaisePropertyChanged("FullName");
                }
            }
        }

        public string LastName
        {
            get
            {
                return _person.LastName;
            }
            set
            {
                if (!_person.LastName.Equals(value))
                {
                    _person.LastName = value;
                    RaisePropertyChanged("LastName");
                    RaisePropertyChanged("FullName");
                }
            }
        }

        public string FullName
        {
            get
            {
                return _person.FirstName + " " + _person.LastName;
            }
        }

        public int NumericId
        {
            get
            {
                return _person.NumericId;
            }
        }

        public string Sex
        {
            get
            {
                return _person.SexString;
            }
            set
            {
                if (!_person.SexString.Equals(value))
                {
                    if (value.Equals("F", StringComparison.OrdinalIgnoreCase))
                    {
                        _person.Sex = Domain.Sex.Female;
                    }
                    else if (value.Equals("M", StringComparison.OrdinalIgnoreCase))
                    {
                        _person.Sex = Domain.Sex.Male;
                    }

                    RaisePropertyChanged("Sex");
                }
            }
        }

        public DateTime? DateBirth
        {
            get
            {
                return _person.DateBirth;
            }
            set
            {
                if (!_person.DateBirth.Equals(value))
                {
                    _person.DateBirth = value;
                    RaisePropertyChanged("DateBirth");
                }
            }
        }

        public bool IsDateBirthEstimated
        {
            get
            {
                return _person.IsDateBirthEstimated;
            }
            set
            {
                if (!_person.IsDateBirthEstimated.Equals(value))
                {
                    _person.IsDateBirthEstimated = value;
                    RaisePropertyChanged("IsDateBirthEstimated");
                }
            }
        }

        public DateTime DateCreated
        {
            get
            {
                return _person.DateCreated;
            }
        }

        public DateTime DateUpdated
        {
            get
            {
                return _person.DateUpdated;
            }
        }

        public string StreetAddress
        {
            get
            {
                return _person.StreetAddress;
            }
            set
            {
                if (!_person.StreetAddress.Equals(value))
                {
                    _person.StreetAddress = value;
                    RaisePropertyChanged("StreetAddress");
                }
            }
        }

        public string City
        {
            get
            {
                return _person.City;
            }
            set
            {
                if (!_person.City.Equals(value))
                {
                    _person.City = value;
                    RaisePropertyChanged("City");
                }
            }
        }

        public string Adm1
        {
            get
            {
                return _person.Adm1;
            }
            set
            {
                if (!_person.Adm1.Equals(value))
                {
                    _person.Adm1 = value;
                    RaisePropertyChanged("Adm1");
                }
            }
        }

        public string Adm2
        {
            get
            {
                return _person.Adm2;
            }
            set
            {
                if (!_person.Adm2.Equals(value))
                {
                    _person.Adm2 = value;
                    RaisePropertyChanged("Adm2");
                }
            }
        }

        public string Adm3
        {
            get
            {
                return _person.Adm3;
            }
            set
            {
                if (!_person.Adm3.Equals(value))
                {
                    _person.Adm3 = value;
                    RaisePropertyChanged("Adm3");
                }
            }
        }

        public string PostalCode
        {
            get
            {
                return _person.PostalCode;
            }
            set
            {
                if (!_person.PostalCode.Equals(value))
                {
                    _person.PostalCode = value;
                    RaisePropertyChanged("PostalCode");
                }
            }
        }

        public string Country
        {
            get
            {
                return _person.Country;
            }
            set
            {
                if (!_person.Country.Equals(value))
                {
                    _person.Country = value;
                    RaisePropertyChanged("Country");
                }
            }
        }

        public string GlobalRecordId
        {
            get
            {
                return _person.GlobalRecordId;
            }
        }
        #endregion // Properties

        #region Methods

        public void InitializeCollectionViews()
        {
            MeasurementsView = new CollectionViewSource { Source = _measurementCollection }.View;
            MeasurementsView.SortDescriptions.Add(new SortDescription("DateMeasurement", ListSortDirection.Descending));

            MeasurementsView.MoveCurrentTo(null);

            MeasurementsView.CurrentChanging += MeasurementsView_CurrentChanging;
            MeasurementsView.CurrentChanged += MeasurementsView_CurrentChanged;

            MeasurementsView.MoveCurrentToFirst();
        }

        private void MeasurementsView_CurrentChanged(object sender, EventArgs e)
        {
            if (MeasurementsView.CurrentItem == null) return;

            var vm = MeasurementsView.CurrentItem as MeasurementViewModel;
            if (vm != null)
            {
                vm.IsEditing = true;
            }
        }

        private void MeasurementsView_CurrentChanging(object sender, CurrentChangingEventArgs e)
        {
            if (MeasurementsView.CurrentItem == null) return;

            var vm = MeasurementsView.CurrentItem as MeasurementViewModel;
            if (vm != null)
            {
                vm.IsEditing = false;
            }
        }
        #endregion // Methods

        #region Commands

        public ICommand RemoveMeasurementCommand { get { return new RelayCommand(RemoveMeasurementCommandExecute, CanExecuteRemoveMeasurementCommand); } }
        private void RemoveMeasurementCommandExecute()
        {
            var mVm = MeasurementsView.CurrentItem as MeasurementViewModel;
            if (mVm != null)
            {
                _measurementCollection.Remove(mVm);

                MeasurementsView.Refresh();

                _person.RemoveMeasurement(new Guid(mVm.GlobalRecordId));
            }

            if (IsShowingRemoveMeasurementPrompt)
            {
                IsShowingRemoveMeasurementPrompt = false;
            }
        }

        public ICommand PromptRemoveMeasurementCommand { get { return new RelayCommand(PromptRemoveMeasurementCommandExecute, CanExecuteRemoveMeasurementCommand); } }
        private void PromptRemoveMeasurementCommandExecute()
        {
            IsShowingRemoveMeasurementPrompt = !IsShowingRemoveMeasurementPrompt;
        }

        public ICommand AddMeasurementCommand { get { return new RelayCommand(AddMeasurementCommandExecute, CanExecuteAddMeasurementCommand); } }
        private void AddMeasurementCommandExecute()
        {
            // add the measurement to the domain model
            var m = new Measurement(Guid.NewGuid().ToString(), _person, DateTime.Now, DateTime.Now)
            {
                DateMeasurement = DateTime.Today
            };
            _person.AddMeasurement(m);

            // create view model for this measurement based on the domain model
            var mVm = new MeasurementViewModel(m) {IsEditing = true};
            _measurementCollection.Add(mVm);

            MeasurementsView.MoveCurrentToFirst();
        }

        private bool CanExecuteRemoveMeasurementCommand()
        {
            if (this.HasErrors) return false;

            if (MeasurementsView != null && MeasurementsView.CurrentItem != null)
            {
                var mVm = MeasurementsView.CurrentItem as MeasurementViewModel;

                if (mVm != null && !mVm.HasErrors)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            return false;
        }

        private bool CanExecuteAddMeasurementCommand()
        {
            if (this.HasErrors) return false;

            if (MeasurementsView != null && MeasurementsView.CurrentItem != null)
            {
                var mVm = MeasurementsView.CurrentItem as MeasurementViewModel;

                if (mVm != null && mVm.HasErrors)
                {
                    return false;
                }
            }

            return true;
        }

        //public ICommand EditMeasurementCommand { get { return new RelayCommand(EditMeasurementCommandExecute, CanExecuteEditMeasurementCommand); } }
        //private void EditMeasurementCommandExecute()
        //{
        //    ((MeasurementViewModel)(MeasurementsView.CurrentItem)).IsEditing = true;
        //}

        //private bool CanExecuteEditMeasurementCommand()
        //{
        //    // TODO: Consider conditions under which this shouldn't be allowed

        //    return true;
        //}
        #endregion // Commands
    }
}
